package cn.stimd.spring.context.annotation;

import cn.stimd.spring.beans.factory.config.BeanDefinitionHolder;
import cn.stimd.spring.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.env.Environment;
import org.springframework.core.type.filter.AbstractTypeHierarchyTraversingFilter;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;

import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.Set;

/**
 * Created by Stimd
 * 项目地址：https://gitee.com/stimd/spring-wheel
 * 公众号：Java编程探微
 */
public class ComponentScanAnnotationParser {
    private final Environment environment;
    private final BeanDefinitionRegistry registry;

    public ComponentScanAnnotationParser(Environment environment, BeanDefinitionRegistry registry) {
        this.environment = environment;
        this.registry = registry;
    }


    /**
     * 解析@ComponnentScan注解，扫描并加载相关组件
     * @param componentScan  @ComponentScan注解上的属性信息
     * @param declaringClass @ComponentScan注解所在的配置类的全类名
     */
    public Set<BeanDefinitionHolder> parse(AnnotationAttributes componentScan, final String declaringClass) {
        ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(this.registry, this.environment);
        Set<String> basePackages = new LinkedHashSet<>();

        //1. 指定需要扫描的路径
        //1.1 处理basePackages
        String[] basePackagesArray = componentScan.getStringArray("basePackages");
        basePackages.addAll(Arrays.asList(basePackagesArray));

        //1.2 处理basePackageClasses
        for (Class<?> clazz : componentScan.getClassArray("basePackageClasses")) {
            basePackages.add(ClassUtils.getPackageName(clazz));
        }

        //1.3 没有指定任何属性，则以声明类作为扫描路径
        if(basePackages.isEmpty()){
            basePackages.add(ClassUtils.getPackageName(declaringClass));
        }

        //2. 扫描时排除当前类
        scanner.addExcludeFilter(new AbstractTypeHierarchyTraversingFilter(false, false) {
            @Override
            protected boolean matchClassName(String className) {
                return declaringClass.equals(className);
            }
        });
        //3. 扫描并加载组件
        return scanner.doScan(StringUtils.toStringArray(basePackages));
    }
}
